﻿using System;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using Duellum.Core;
using Duellum.Map;
using JpLabs.Geometry;

namespace Duellum.Wpf2d.Plugin
{
    public class MapRenderer : BaseMapSingleRenderer
    {
		MapScrollViewer mapViewer;
		
		public MapRenderer(MapScrollViewer mapViewer)
		{
			this.mapViewer = mapViewer;
		}

		protected override int VisualZOrder { get { return MapZOrder.BottomMost; } } //map is always bottom-most

		protected override OverlayVisual CreateVisual(int zOrder)
		{
			var visual = base.CreateVisual(zOrder);
			RenderOptions.SetBitmapScalingMode(visual, BitmapScalingMode.HighQuality);
			RenderOptions.SetEdgeMode(visual, EdgeMode.Aliased);
			return visual;
		}

		public override Size? GetMeasure()
		{
			return Wpf2dPlugin.GetMapSize(mapViewer.Map, mapViewer.Level);
		}

		public override void OnRender()
		{
			//*/
			Visual.Render(dc => RenderMap(dc));
			
			//StreamGeometry
			
			/*/
			using (var dc0 = Visual.RenderOpen()) {
			
				var imgGroup = new DrawingGroup();
				var imgSource = new DrawingImage(imgGroup);

				RenderOptions.SetBitmapScalingMode(imgGroup, BitmapScalingMode.LowQuality);
				RenderOptions.SetEdgeMode(imgGroup, EdgeMode.Aliased);
				
				using (var dc1 = imgGroup.Open()) RenderMap(dc1);
				
				var img = imgSource.CloneCurrentValue();

				dc0.DrawImage(img, new Rect(new Size(img.Width, img.Height)));
			}
			//*/
		}

		private void RenderMap(DrawingContext dc)
		{
			var domain = mapViewer.DuelDomain;
			if (domain == null) return;
			
			var wpf2d = domain.GetPlugin<Wpf2dPlugin>(); //Wpf2dPlugin.Current;
			if (wpf2d == null) return;
			
			var hexSize = wpf2d.HexSize;
			
			var map = mapViewer.Map;
			if (map != null) {
				int level = mapViewer.Level;
				var levelViewPort = new BoundingBoxInt(
					new PointInt(0, 0, level),
					new PointInt(map.DimX, map.DimY, level)
				);

				//TODO: upgrade map so that its shape is more flexible (currently, only a box is possible)
				var drawBox = map.Box & levelViewPort;
				
				int groundLevel = map.GroundLevel;
				bool isAboveGround	= (level > groundLevel);
				bool isOnGround		= (level == groundLevel);
				bool isUnderGround	= (level < groundLevel);
				
				bool [,] hasGroundFlags = null;
				int lowerX = drawBox.Lower.X;
				int lowerY = drawBox.Lower.Y;
				if (!isOnGround) {
					var size = drawBox.Upper - drawBox.Lower;
					hasGroundFlags = new bool[size.X + 1, size.Y + 1];
				}
				
				//DuelTypeId groundBaseType = DuelCoreTypes.Ground.Base;//DuelDomain.Current.GetType(DuelCoreTypes.Ground.Base);

				//Draw map content
				//var content = map.Query(levelViewPort).ToArray();
				//foreach (var e in content) {
				
				/*				
				foreach (var e in map.Query(levelViewPort)) {
					if (!isOnGround) {
						if (e.Object.IsGround()) {//IsDescendantOf(groundBaseType)) {
							var posBuffer = e.Position - drawBox.Lower;
							groundBuffer[posBuffer.X, posBuffer.Y] = true;
						}
					}
					
					var image = wpf2d.GetImage(e.Object);
					
					if (image != null) {
						var p = Wpf2dPlugin.PositionToUpperLeftPoint(e.Position.X, e.Position.Y);
						var rect = new Rect(p, hexSize);
						dc.DrawImage(image, rect);
					}
				}
				
				//Draw empty positions and hex borders
				var zoom = mapViewer.Zoom;
				var hexBorderImg	= wpf2d.GetHexBorderImg(zoom);
				//var noFloorImg		= wpf2d.GetNoFloorImg(zoom);
				//var undergroundImg	= wpf2d.GetNoFloorImg(zoom); //TODO: underground image
				
				for (int x=drawBox.Lower.X; x<=drawBox.Upper.X; x++)
				for (int y=drawBox.Lower.Y; y<=drawBox.Upper.Y; y++)
				{
					BitmapSource img
						= (isOnGround || groundBuffer[x - lowerX, y - lowerY])
						? hexBorderImg
						: wpf2d.GetEmptyHexImg(level, groundLevel, zoom);//(isAboveGround) ? noFloorImg : undergroundImg;
					
					var p = Wpf2dPlugin.PositionToUpperLeftPoint(x, y);
					var rect = new Rect(p, hexSize);
					dc.DrawImage(img, rect);
				}
				/*/
				var query = map.Query(levelViewPort).GroupBy(e => e.Position).ToArray();
				//var debug = query.Select(g => g.Key).ToArray();
				
				var zoom = mapViewer.Zoom;
				
				foreach (var group in query) {
					var pos = group.Key;
					if (!isOnGround) {
						if (group.WhereIsGround().Any()) {
							var posAtBuffer = pos - drawBox.Lower;
							hasGroundFlags[posAtBuffer.X, posAtBuffer.Y] = true;
						}
					}
					
					var image = wpf2d.GetImage(group.GetObjects(), pos, groundLevel, zoom);
					
					if (image != null) {
						var p = Wpf2dPlugin.PositionToOriginPoint(pos);
						var rect = new Rect(p, hexSize);
						dc.DrawImage(image, rect);
					}
				}
				
				//Draw empty cells
				var emptyHexImg = wpf2d.GetEmptyHexImg(level, groundLevel, zoom);
				
				for (int x=drawBox.Lower.X; x<=drawBox.Upper.X; x++)
				for (int y=drawBox.Lower.Y; y<=drawBox.Upper.Y; y++)
				{
					if (isOnGround || !hasGroundFlags[x - lowerX, y - lowerY]) {
						var p = Wpf2dPlugin.PositionToOriginPoint(x, y);
						var rect = new Rect(p, hexSize);
						dc.DrawImage(emptyHexImg, rect);
					}
				}
				//*/
				
				//if (debugCursors != null) {
				//    foreach (var c in debugCursors) {
				//        var image = imageCache.Get(Wpf2dPlugin.BuildUri("/res/cacofiend.bmp"));
				//        var p = PositionToPoint(c.X, c.Y);
				//        var rect = new Rect(p + new Vector(5, 5), HexSize.Scale(0.5));
				//        dc.DrawImage(image, rect);
				//    }
				//}
			}
						
			//var brush = new SolidColorBrush(Color.FromArgb(127, 255, 0, 0));
			//var geometry = new RectangleGeometry(rect);
			//dc.DrawGeometry(brush, null, geometry);
		}
	}
}
